package org.jianfa.mq.seckill.redis;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * redis service封装对redis的操作，如get、set、incr等
 */

@Service
public class RedisService {

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    /**
     * redis 的get操作，通过key获取存储在redis中的对象
     *
     * @param prefix key的前缀
     * @param key    业务层传入的key
     * @param clazz  存储在redis中的对象类型（redis中是以字符串存储的）
     * @param <T>    指定对象对应的类型
     * @return 存储于redis中的对象
     */
    public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {
        // 生成真正的存储于redis中的key
        String realKey = prefix.getPrefix() + ":" + key;
        // 通过key获取存储于redis中的对象（这个对象是以json格式存储的，所以是字符串）
        String strValue = redisTemplate.boundValueOps(realKey).get();
        // 将json字符串转换为对应的对象
        T objValue = stringToBean(strValue, clazz);
        return objValue;
    }

    /**
     * redis的set操作
     *
     * @param prefix 键的前缀
     * @param key    键
     * @param value  值
     * @return 操作成功为true，否则为true
     */
    public <T> boolean set(KeyPrefix prefix, String key, T value) {
        // 将对象转换为json字符串
        String strValue = beanToString(value);

        if (strValue == null || strValue.length() <= 0)
            return false;

        // 生成实际存储于redis中的key
        String realKey = prefix.getPrefix() + ":" + key;
        // 获取key的过期时间
        int seconds = prefix.expireSeconds();

        if (seconds <= 0) {
            // 设置key的过期时间为redis默认值（由redis的缓存策略控制）
            redisTemplate.boundValueOps(realKey).set(strValue);
        } else {
            // 设置key的过期时间
            redisTemplate.boundValueOps(realKey).set(strValue, seconds, TimeUnit.SECONDS);
        }

        return true;
    }


    /**
     * 判断key是否存在于redis中
     *
     * @param keyPrefix key的前缀
     * @param key
     * @param <T>
     * @return
     */
    public <T> boolean exists(KeyPrefix keyPrefix, String key) {
        String realKey = keyPrefix.getPrefix() + ":" + key;
        return redisTemplate.opsForValue().getOperations().hasKey(realKey);
    }


    /**
     * 自增
     *
     * @param keyPrefix
     * @param key
     * @param <T>
     * @return
     */
    public <T> Long incr(KeyPrefix keyPrefix, String key) {
        String realKey = keyPrefix.getPrefix() + ":" + key;
        return redisTemplate.opsForValue().increment(realKey);
    }


    /**
     * 自减
     *
     * @param keyPrefix
     * @param key
     * @param <T>
     * @return
     */
    public <T> Long decr(KeyPrefix keyPrefix, String key) {
        String realKey = keyPrefix.getPrefix() + ":" + key;
        return redisTemplate.opsForValue().decrement(realKey);
    }

    /**
     * 删除缓存中的用户数据
     *
     * @param keyPrefix
     * @param key
     * @return
     */
    public boolean delete(KeyPrefix keyPrefix, String key) {
        String realKey = keyPrefix.getPrefix() + ":" + key;
        return redisTemplate.opsForValue().getOperations().delete(realKey);
    }

    /**
     * 将对象转换为对应的json字符串
     *
     * @param value 对象
     * @param <T>   对象的类型
     * @return 对象对应的json字符串
     */
    public static <T> String beanToString(T value) {
        if (value == null)
            return null;

        Class<?> clazz = value.getClass();
        /*首先对基本类型处理*/
        if (clazz == int.class || clazz == Integer.class)
            return "" + value;
        else if (clazz == long.class || clazz == Long.class)
            return "" + value;
        else if (clazz == String.class)
            return (String) value;
            /*然后对Object类型的对象处理*/
        else
            return JSON.toJSONString(value);
    }

    /**
     * 根据传入的class参数，将json字符串转换为对应类型的对象
     *
     * @param strValue json字符串
     * @param clazz    类型
     * @param <T>      类型参数
     * @return json字符串对应的对象
     */
    public static <T> T stringToBean(String strValue, Class<T> clazz) {

        if ((strValue == null) || (strValue.length() <= 0) || (clazz == null))
            return null;

        // int or Integer
        if ((clazz == int.class) || (clazz == Integer.class))
            return (T) Integer.valueOf(strValue);
            // long or Long
        else if ((clazz == long.class) || (clazz == Long.class))
            return (T) Long.valueOf(strValue);
            // String
        else if (clazz == String.class)
            return (T) strValue;
            // 对象类型
        else
            return JSON.toJavaObject(JSON.parseObject(strValue), clazz);
    }
}
